#include <fmt/format.h>
#include "../opengl/opengl.hpp"
#include "../gl/Camera.hpp"
#include "../gl/Window.hpp"

namespace gl = opengl;

int main() {
    Window window("OpenGL", 800, 600);
    gl::init_opengl(gl::opengl_config{.load_function = &SDL_GL_GetProcAddress,
                                      .debug_msg_callback = [](auto, auto, auto id, auto, auto, auto msg, auto) {
                                          fmt::println("[OpenGL Object {}] {}", id, msg);
                                      }});

    gl::vertex_array vao({
        gl::vertex_attribute(0, gl::type_f32, 3, 0),
        gl::vertex_attribute(1, gl::type_f32, 3, 12),
    });
    // postion: vec3, color: vec3
    gl::f32 data[6 * 4] = {
        0.5,  0.5,  0.0, 1.0, 0.0, 0.0, // Vertex 1
        0.5,  -0.5, 0.0, 0.0, 1.0, 0.0, // Vertex 2
        -0.5, -0.5, 0.0, 0.0, 0.0, 1.0, // Vertex 3
        -0.5, 0.5,  0.0, 1.0, 1.0, 0.0, // Vertex 4
    };
    gl::u32 index[] = {
        0, 1, 2, // First triangle
        0, 2, 3, // Second triangle
    };
    gl::buffer vertices(sizeof(data), data);
    gl::buffer indices(sizeof(index), index);
    gl::program program({gl::shader::from_file(gl::shader::vertex_shader, "../shader/camera/vs.glsl"),
                         gl::shader::from_file(gl::shader::fragment_shader, "../shader/camera/fs.glsl")});
    PerspectiveCamera camera;
    camera.set_position({0.0f, 0.0f, 2.0f});
    camera.look_at(glm::vec3{0.0f});

    window.on_exec = [&](auto) {
        auto [w, h] = window.get_size();
        gl::api::glViewport(0, 0, w, h);
        camera.set_aspect_ratio((float) w / (float) h);
    };
    window.on_resize = [&](auto, int w, int h) {
        gl::api::glViewport(0, 0, w, h);
        camera.set_aspect_ratio((float) w / (float) h);
    };
    window.on_render = [&](auto) {
        gl::clear_buffers();
        vao.bind();
        program.use();
        program.set_mat4f(0, glm::value_ptr(camera.viewProjectionMatrix));
        vao.set_vertex_buffer(vertices, sizeof(data) / 4);
        vao.set_index_buffer(indices);
        gl::api::glDrawElements(gl::triangles, 6, gl::type_u32, nullptr);
    };
    window.on_process_event = [&](auto, const SDL_Event *event) {
        if (event->type == SDL_EventType::SDL_MOUSEWHEEL) {
            constexpr float sensitivity = 2.0f;
            auto fov = glm::clamp(camera.fov - sensitivity * event->wheel.preciseY, 15.0f, 85.0f);
            camera.set_fov(fov);
        }
    };
    
    return window.exec();
}